package com.bruce.tool.office.excel;

import com.bruce.tool.common.CommonApplication;
import com.bruce.tool.common.util.LogUtils;
import com.bruce.tool.common.util.SpringBeanUtils;
import com.bruce.tool.common.util.file.core.TXT;
import com.bruce.tool.common.util.string.StringUtils;
import com.bruce.tool.office.excel.constant.SheetInfo;
import com.bruce.tool.office.excel.core.Excels;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.List;
import java.util.Map;

/**
 * 功能 :
 *
 * @author : Bruce(刘正航) 9:32 PM 2018/12/27
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CommonApplication.class)
@AutoConfigureMockMvc
@WebAppConfiguration
public class PhoneNumberHandler extends AbstractJUnit4SpringContextTests {

    private String basePath = System.getProperty("user.home")+"/Desktop/电话号码/";

    @Before
    public void init(){
        SpringBeanUtils.setApplicationContext(applicationContext);
    }

    /**过滤电话号码:将非中国号码过滤掉**/
    @Test
    public void stepOne(){
        long begin = System.currentTimeMillis();
        List<String> lines = Lists.newArrayList();
        TXT.create().path(basePath+"1.txt").lineIterator((count, index, line) -> {
                    if(StringUtils.isBlank(line)){
                        return true;
                    }
                    if( !line.matches("1[0-9]{10}") ){
                        return true;
                    }
                    lines.add(line);
                    if(index % 1000000 == 0){
                        TXT.create().path(basePath+"3.txt").writeLines(lines);
                        lines.clear();
                        LogUtils.info(log,"已获取10000行数据,执行一次写入文件操作");
                    }
                    return true;
                });
        TXT.create().path(basePath+"3.txt").writeLines(lines);
        lines.clear();
        LogUtils.info(log,"数据写入完毕,耗时:{}",System.currentTimeMillis()-begin);
    }

    /**
     * 拆分为多个文件的目的:
     * 1.大文件无法执行awk乱序命令,所以要拆分成小文件.
     * 2.拆分的过程中需要保证每一个小文件都是均匀乱序的.
     */
    @Test
    public void stepTwo(){
        long begin = System.currentTimeMillis();
        List<String> ones = Lists.newArrayList();
        List<String> twos = Lists.newArrayList();
        List<String> threes = Lists.newArrayList();
        List<String> fours = Lists.newArrayList();
        List<String> fives = Lists.newArrayList();
        List<String> sixs = Lists.newArrayList();
        List<String> seven = Lists.newArrayList();
        TXT.create().path(basePath+"3.txt").lineIterator((count, index, line) -> {
                    if(StringUtils.isBlank(line)){
                        return true;
                    }
                    Integer num = (int)(Math.random() * 7);
                    if(num == 6){ seven.add(line); }else
                    if(num == 5){ sixs.add(line); }else
                    if(num == 4){ fives.add(line); }else
                    if(num == 3){ fours.add(line); }else
                    if(num == 2){ threes.add(line); }else
                    if(num == 1){ twos.add(line); }else
                    if(num == 0){ ones.add(line); }
                    if(ones.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part1.txt").writeLines(ones);
                        ones.clear();
                    }
                    if(twos.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part2.txt").writeLines(twos);
                        twos.clear();
                    }
                    if(threes.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part3.txt").writeLines(threes);
                        threes.clear();
                    }
                    if(fours.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part4.txt").writeLines(fours);
                        fours.clear();
                    }
                    if(fives.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part5.txt").writeLines(fives);
                        fives.clear();
                    }
                    if(sixs.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part6.txt").writeLines(sixs);
                        sixs.clear();
                    }
                    if(seven.size() % 100000 == 0){
                        TXT.create().path(basePath+"phone-part7.txt").writeLines(seven);
                        seven.clear();
                    }
                    return true;
                });

        TXT.create().path(basePath+"phone-part1.txt").writeLines(ones);
        ones.clear();
        TXT.create().path(basePath+"phone-part2.txt").writeLines(twos);
        twos.clear();
        TXT.create().path(basePath+"phone-part3.txt").writeLines(threes);
        threes.clear();
        TXT.create().path(basePath+"phone-part4.txt").writeLines(fours);
        fours.clear();
        TXT.create().path(basePath+"phone-part5.txt").writeLines(fives);
        fives.clear();
        TXT.create().path(basePath+"phone-part6.txt").writeLines(sixs);
        sixs.clear();
        TXT.create().path(basePath+"phone-part7.txt").writeLines(seven);
        seven.clear();
        LogUtils.info(log,"数据写入完毕,耗时:{}",System.currentTimeMillis()-begin);
    }

    /**执行shell命令,打乱文件内容**/
    @Test
    public void stepThree(){
        /**
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part1.txt>>order-phone-1.txt;
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part2.txt>>order-phone-2.txt;
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part3.txt>>order-phone-3.txt;
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part4.txt>>order-phone-4.txt;
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part5.txt>>order-phone-5.txt;
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part6.txt>>order-phone-6.txt;
         awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' phone-part7.txt>>order-phone-7.txt;
         */
    }

    /**
     * 功能:
     * 1.根据日期文件,获取每个月份,需要生成的记录总数
     * 2.通过乱序的电话号码,生成按月份的乱序电话号码
     */
    @Test
    public void stepFour() throws FileNotFoundException {
        List<SheetInfo> sheetInfos = Excels.Import().config(new FileInputStream(basePath+"日期.xlsx"))
                .addSheet(new Integer[]{3})
                .executeForAll();
        String[] phoneParts = new String[]{
                "order-phone-1.txt",
                "order-phone-2.txt",
                "order-phone-3.txt",
                "order-phone-4.txt",
                "order-phone-5.txt",
                "order-phone-6.txt",
                "order-phone-7.txt"
        };
        for (SheetInfo sheetInfo : sheetInfos) {
            Long count = getMonthDataCount(sheetInfo);
            // 生成result-yue.txt文件
            String phoneFileName = basePath+"result-"+sheetInfo.getName()+".txt";
            LogUtils.info(log,"最终结果文件,按月生成:{}",phoneFileName);
            List<String> phonelines = Lists.newArrayList();
            List<Long> curLineIndex = Lists.newArrayList(0L);
            for (String monthFile : phoneParts) {
                String groupFileName = basePath + monthFile;
                LogUtils.info(log,"原始乱序数据文件:{}",groupFileName);
                TXT.create().path(groupFileName).lineIterator((total, index, line) -> {
                    // 批量写入文件
                    if( index % 1000000 == 0 ){
                        LogUtils.info(log,"当前写入的文件名:{},需要的总行数:{},当前文件:{},当前行数:{},满足100万,写一次数据",phoneFileName,count,monthFile,index);
                        TXT.create().path(phoneFileName).writeLines(phonelines);
                        phonelines.clear();
                        return true;
                    }
                    // 文件结束标记
                    if( total.equals(index + 1) ){
                        LogUtils.info(log,"当前写入的文件名:{},需要的总行数:{},当前文件:{},当前行数:{},文件读完,接着读取下一个文件",phoneFileName,count,monthFile,index);
                        TXT.create().path(phoneFileName).writeLines(phonelines);
                        phonelines.clear();
                        return true;
                    }
                    // 总数量判断
                    Long totalCount = curLineIndex.get(0);
                    if( totalCount >= count ){
                        LogUtils.info(log,"当前写入的文件名:{},需要的总行数:{},当前文件:{},当前行数:{},满足总数,最后一次写入",phoneFileName,count,monthFile,index);
                        TXT.create().path(phoneFileName).writeLines(phonelines);
                        phonelines.clear();
                        return false;
                    }
                    // 正常的数据累加
                    curLineIndex.set(0,totalCount + 1);
                    phonelines.add(line);
                    return true;
                });

                Long totalCount = curLineIndex.get(0);
                if( totalCount >= count ){
                    LogUtils.info(log,"当前写入的文件名:{},需要的总行数:{},当前文件:{},写入当前文件完毕",phoneFileName,count,monthFile);
                    break;
                }
            }
        }
    }

    /**
     * 1.读取excel文件,生成状态文件,乱序之后,在第6步使用
     */
    @Test
    public void stepFive() throws Exception {
        List<SheetInfo> sheetInfos = Excels.Import().config(new FileInputStream(basePath+"日期.xlsx"))
                .addSheet(new Integer[]{3})
                .executeForAll();
        SheetInfo sign = Excels.Import().config(new FileInputStream(basePath+"状态生成.xlsx"))
                .addSheet(new Integer[]{0,1,2})
                .executeForActive();
        // 1.获取excel数据
        for (SheetInfo sheetInfo : sheetInfos) {
            Long count = getMonthDataCount(sheetInfo);
            List datas = sign.getDatas();
            List<String> backStatus = Lists.newArrayList();
            Map<String,Long> backStatusNum = Maps.newConcurrentMap();
            for (Object data : datas) {
                if( data instanceof List ){
                    List item = (List)data;
                    if( item.size() == 2 ){ continue; }
                    Object cellValue = item.get(0);
                    String status = String.valueOf(cellValue);
                    backStatus.add(status);
                    Object number = item.get(2);
                    // 核心计算逻辑: 百分比 上下浮动 10%-20%
                    Double result = (((Number)number).doubleValue()) * (1 + Math.random() * 0.2 - 0.1);
                    // 浮动之后的数量: 浮动之后的结果 * 总数量
                    Long rows = ((Double)(result * count)).longValue();
                    backStatusNum.put(status,rows);
                }
            }

            Long realTotal = 0L;
            List<String> statusLines = Lists.newArrayList();
            String statusFileName = basePath+"status-result-"+sheetInfo.getName()+".txt";
            // 填充指定的状态值
            for (String status : backStatus) {
                Long step = backStatusNum.get(status);
                for (int i=0;i<step-1;i++) {
                    statusLines.add(status);
                    realTotal ++;
                    if(i % 1000000 == 0){
                        TXT.create().path(statusFileName).writeLines(statusLines);
                        statusLines.clear();
                    }
                }
                TXT.create().path(statusFileName).writeLines(statusLines);
                statusLines.clear();
            }
            // 最后补充状态值为0的行
            for (long i = 0,length = (count - realTotal);i < length;i++) {
                statusLines.add("0");
                if(i % 1000000 == 0){
                    TXT.create().path(statusFileName).writeLines(statusLines);
                    statusLines.clear();
                }
            }
            TXT.create().path(statusFileName).writeLines(statusLines);
            statusLines.clear();
            LogUtils.info(log,"结果集文件名:{}",statusFileName);
        }
    }

    /**
     * 1.乱序状态文件,按月乱序
     */
    @Test
    public void stepSix() {
        /**
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-4yue.txt >> order-status-result-4yue.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-5yue.txt >> order-status-result-5yue.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-6yue.txt >> order-status-result-6yue.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-7yue.txt >> order-status-result-7yue.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-8yue.txt >> order-status-result-8yue.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-9yue.txt >> order-status-result-9yue.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-apr.txt >> order-status-result-apr.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-aug.txt >> order-status-result-aug.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-eight.txt >> order-status-result-eight.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-feb.txt >> order-status-result-feb.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-five.txt >> order-status-result-five.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-four.txt >> order-status-result-four.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-jan.txt >> order-status-result-jan.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-jul.txt >> order-status-result-jul.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-jun.txt >> order-status-result-jun.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-mar.txt >> order-status-result-mar.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-may.txt >> order-status-result-may.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-nine.txt >> order-status-result-nine.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-sep.txt >> order-status-result-sep.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-seven.txt >> order-status-result-seven.txt;
        awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' status-result-six.txt >> order-status-result-six.txt;
        */
    }

    /**
     * 0.拼接文本,形成最终结果
     * 1.读取excel文件,获取总行数,获取月份日期,写入txt文件
     */
    @Test
    public void stepSeven() throws Exception {
        List<SheetInfo> sheetInfos = Excels.Import().config(new FileInputStream(basePath+"日期.xlsx"))
                .addSheet(new Integer[]{1,3})
                .executeForAll();
        for (SheetInfo sheetInfo : sheetInfos) {
            List<String> dates = Lists.newArrayList();
            Map<String,Long> numDate = Maps.newConcurrentMap();
            for (Object row : sheetInfo.getDatas()) {
                if( row instanceof List ){
                    String date = (String) ((List) row).get(0);
                    Number num = (Number) ((List) row).get(1);
                    numDate.put(date,num.longValue());
                    dates.add(date);
                }
            }
            String fileName = basePath+"result-"+sheetInfo.getName()+".txt";
            LogUtils.info(log,"乱序手机号文件名:{}",fileName);
            List<String> phones = Lists.newArrayList();
            List<Integer> curIndex = Lists.newArrayList(0);
            TXT.create().path(fileName).lineIterator((count, index, line) -> {
                StringBuilder phone = new StringBuilder();
                phone.append("\"");
                phone.append(line);
                phone.replace(4,8,"****");
                phone.append("\"\t");
                phone.append("\"status\"\t");
                String date = updateDateCount(dates, numDate);
                phone.append("\"");
                phone.append(date);
                phone.append("\"");
                phones.add(phone.toString());
                if( index % 1000000 == 0 ){
                    String statusFileName = basePath+"order-status-result-"+sheetInfo.getName()+".txt";
                    LogUtils.info(log,"乱序状态文件名:{}",statusFileName);
                    List<String> statuss = Lists.newArrayList();
                    String resultFile = basePath+"finish-"+sheetInfo.getName()+".txt";
                    TXT.create().path(statusFileName).lineIterator((total, sindex, sline) -> {
                        if( curIndex.get(0) > sindex ){ return true; }
                        String sphone = phones.get((int) ((sindex % 1000000)));
                        if( sline.endsWith(".0") ){ sline = sline.replace(".0",""); }
                        sphone = sphone.replace("status",sline);
                        statuss.add(sphone);
                        if( sindex.equals(index) ){
                            TXT.create().path(resultFile).writeLines(statuss);
                            statuss.clear();
                            return false;
                        }
                        return true;
                    });
                    TXT.create().path(resultFile).writeLines(statuss);
                    statuss.clear();
                    phones.clear();
                    curIndex.set(0,index.intValue());
                }
                return true;
            });

            String statusFileName = basePath+"order-status-result-"+sheetInfo.getName()+".txt";
            LogUtils.info(log,"乱序状态文件名:{}",statusFileName);
            List<String> statuss = Lists.newArrayList();
            String resultFile = basePath+"finish-"+sheetInfo.getName()+".txt";
            TXT.create().path(statusFileName).lineIterator((count, sindex, sline) -> {
                if( curIndex.get(0) > sindex ){ return true; }
                Long lsindex = (sindex % 1000000);
                if( lsindex > phones.size() - 1 ){ return false; }
                String sphone = phones.get(lsindex.intValue());
                if( sline.endsWith(".0") ){ sline = sline.replace(".0",""); }
                sphone = sphone.replace("status",sline);
                statuss.add(sphone);
                return true;
            });
            TXT.create().path(resultFile).writeLines(statuss);
            statuss.clear();
        }
    }

    /**获取月数据总量**/
    private Long getMonthDataCount(SheetInfo sheetInfo) {
        Long count = 0L;
        for(Object data : sheetInfo.getDatas()){
            if( data instanceof List){
                count += ((Number)((List)data).get(0)).longValue();
            }
        }
        return count;
    }

    /**
     * 更新日期行数
     * 逻辑:
     * 每取一次日期,把日期对应的总数减一,直到用完总数为止
     */
    private String updateDateCount(List<String> dates, Map<String, Long> numDate) {
        String date = dates.get(0);
        Long num = numDate.get(date) - 1;
        if( num <= 0 ){
            dates.remove(date);
            numDate.remove(date);
        }else{
            numDate.put(date,num);
        }
        return date;
    }

}
